// **************************************************************
// Com ++ Framework by the C++ World
// Please report defects: mailto:compp@cplusplusworld.com
// Please visit: http://www.cplusplusworld.com/socketlibrary.html
// **************************************************************

#include <iostream>
#include <errno.h>
#include <string.h>

//#include "config.h"
#include "SysPlusPlus/syscall.h"
#include <stdio.h>
#include "ComPlusPlus/Sem.h"
#include "SysPlusPlus/ComException.h"

#include <semaphore.h>

/**
 * Constructor for Generic Semaphore
 * Initialises Semaphore with a value of zero
 */
compp::Sem::Sem( bool remote, const int id){

//  SemId = NULL ;

  if ( remote ) {
    sm = new compp::ShMem ( id, sizeof ( sem_t) ,0644, false  ) ;
    sm->Attach ();
    if ( NULL == ( SemIdP = (sem_t *) sm->getStartAddr() )) 
      throw syspp::ComException ( "Cannot create shared Sempahore." );
    
    // Find out, if the segment was inherited or created.
    sem_t dummy;
    memset ( (void*) &dummy, 0, sizeof ( dummy ) );
    
    if ( 0 == memcmp ( (void*) &dummy, (void*) SemIdP, sizeof (dummy) ) ) {
      // New one
      if ( 0 != syspp::Call::Sem_init( SemIdP, 1, 0) ) {
	perror ( "Sem_Init");
	throw syspp::ComException ( "Cannot create local Sempahore." );
      }
    }

  } else {
    SemIdP = & SemId;    
    sm = NULL;
    if ( 0 != syspp::Call::Sem_init( SemIdP, 1, 0) ) {
      perror ( "Sem_Init");
      throw syspp::ComException ( "Cannot create local Sempahore." );
    }
  }
}

/**
  * Constructor for Generic Semaphore
  * Initialises Semaphore with a value
  */

compp::Sem::Sem(const int Val, bool remote, const int id ) {

  //SemId = NULL ;
  if ( remote ) {
    sm = new compp::ShMem ( id, sizeof ( sem_t) ,0644, false  ) ;
    sm->Attach ();
    if ( NULL == ( SemIdP = (sem_t *) sm->getStartAddr())) 
      throw syspp::ComException ( "Cannot create shared Sempahores." );
    
    // Find out, if the segment was inherited or created.
    sem_t dummy;
    memset ( (void*) &dummy, 0, sizeof ( dummy ) );
    
    if ( 0 == memcmp ( (void*) &dummy, (void*) SemIdP, sizeof (dummy) ) ) {
      // New one
      if ( 0 != syspp::Call::Sem_init( SemIdP, 1, 0) ) {
	perror ( "Sem_Init");
	throw syspp::ComException ( "Cannot create local Sempahore." );
      }
    }
    
  } else {
    SemIdP = & SemId;    
    sm = NULL;
    if ( 0 != syspp::Call::Sem_init( SemIdP, 1, Val) ) {
 
    }
  }
}

/**
  * Destructor
  * Entirely deletes the semaphore from the system.
  */
compp::Sem::~Sem(){

  syspp::Call::Sem_destroy ( SemIdP );
  if ( sm != NULL )
    delete sm;
}

/**
  * "Posts" the sempaphore i.e. increases the value
  * by one
  */
bool compp::Sem::Post(){

  if ( 0 != syspp::Call::Sem_post ( SemIdP ) ) {
    return false;
  }
  
  return true;
}

/**
  * "Posts" the sempaphore i.e. increases the value
  * by count
  */
bool compp::Sem::Post(int count){

  for ( int i=0; i <count; ++i ){ 
    if ( 0 != syspp::Call::Sem_post ( SemIdP ) ) {
      return false;
    }
  }
  
  return true;
}

/**
  * Waits for the sempaphore i.e. decreases the value
  * by one.
  * If the value of the semaphore was zero, the 
  * method blocks until the semahore is increased by
  * a "post"
  */
bool compp::Sem::Wait(){

  if ( syspp::Call::Sem_wait(SemIdP) != 0 ) {
    return false;
  }
  return true;
}

/**
  * This method does NOT BLOCK.
  * Return true if semaphore was posted otherwise false.
  */
bool compp::Sem::TryWait(){

  if ( syspp::Call::Sem_trywait(SemIdP) != 0 ) {
    return false;
  }
  return true;
}

/**
  * Waits for the sempaphore i.e. decreases the value
  * by count.
  * If the value of the semaphore was zero, the 
  * method blocks until the semahore is increased by
  * a "post"
  */
int compp::Sem::Wait (int count) {
  int i=0;
  if ( count <= 0)
    return 0;

  for ( i=1; i <= count; ++i ) {
    if ( 0 != syspp::Call::Sem_trywait ( SemIdP ) ) {
      if ( errno == EAGAIN )
	return i;
      else
	return -1;
    }
  }
  
  return i-1;
}



#ifdef SPECIALDEBUG

int main ( int argc, char *argv [] ) {
  int i;


  gen::Sem *g = new gen::Sem (0);

  for ( i = 1 ; i < 100; ++ i ) {

    g->Post();
    g->Wait();

  } 

  return 0;
}

#endif
